#include "../AsyncLogging.h"
using namespace yhaida;
AsyncLogging::AsyncLogging(const std::string &baseName,
                           off_t rollSize,
                           int flushInterval)
    : _start(false),
      _baseName(baseName),
      _rollSize(rollSize),
      _flushInterval(flushInterval),
      _thread(std::bind(&yhaida::AsyncLogging::threadFunc, this), "Logging"),
      _mutex(),
      _cv(_mutex),
      _latch(1),
      _A_Buffer(new Buffer()),
      _B_Buffer(new Buffer()),
      _bufferVector()
{
    _A_Buffer->reset();
    _B_Buffer->reset();
    _bufferVector.reserve(16);
}
AsyncLogging::~AsyncLogging()
{
    if (_start)
    {
        stop();
    }
}
void AsyncLogging::push_logger(const char *str, int length)
{
    MutexLockGuard guard(_mutex);
    if (_A_Buffer->intVail() > length)
    {
        _A_Buffer->push_data(str, length);
    }
    else
    {
        _bufferVector.push_back(std::move(_A_Buffer));
        if (_B_Buffer)
        {
            _A_Buffer = std::move(_B_Buffer);
        }
        else
        {
            _A_Buffer.reset(new Buffer());
        }
        _A_Buffer->push_data(str, length);
        _cv.notify();
    }
}
void AsyncLogging::start()
{
    assert(!_start);
    _start = true;
    _thread.start();
    _latch.wait();
}
void AsyncLogging::stop()
{
    assert(_start);
    _start = false;
    _cv.notifyAll();
    _thread.join();
}
void AsyncLogging::threadFunc()
{
    assert(_start);
    _latch.downLatch();


    BufferPtr _C_Buffer(new Buffer());
    BufferPtr _D_Buffer(new Buffer());
    _C_Buffer->setZero();
    _D_Buffer->setZero();
    BufferVector _writeBuffer;
    _writeBuffer.reserve(16);

    LogFile _file(_baseName, _rollSize, _flushInterval, false); // 线程不安全fasle->单线程

    while (_start)
    {
        assert(_C_Buffer && _C_Buffer->length() == 0);
        assert(_D_Buffer && _D_Buffer->length() == 0);
        assert(_writeBuffer.empty());

        {
            MutexLockGuard guard(_mutex); //多线程
            if (_writeBuffer.empty())
            {
                _cv.waitForSeconds(_flushInterval);
            }
            _bufferVector.push_back(std::move(_A_Buffer));
            _A_Buffer = std::move(_C_Buffer);
            _writeBuffer.swap(_bufferVector);

            if (!_B_Buffer)
            {
                _B_Buffer = std::move(_D_Buffer);
            }
        }
        assert(!_writeBuffer.empty());
        if (_writeBuffer.size() > 25)
        {
            char errorBuff[256];
            int length = snprintf(errorBuff, sizeof(errorBuff),
                                  "Dropped log messages at %s, %zd larger buffers\n",
                                  Timestamp::now().toFormattedString().c_str(), _writeBuffer.size() - 2);
            fputs(errorBuff, stderr);
            _file.write(errorBuff, length);
            _writeBuffer.erase(_writeBuffer.begin() + 2, _writeBuffer.end());
        }
        for (const auto &buf : _writeBuffer)
        {
            _file.write(buf->data(), buf->length());
        }
        if (_writeBuffer.size() > 2)
        {
            _writeBuffer.resize(2);
        }
        if (!_C_Buffer)
        {
            assert(!_writeBuffer.empty());
            _C_Buffer = std::move(_writeBuffer.back());
            _writeBuffer.pop_back();
            _C_Buffer->reset();
        }
        if (!_D_Buffer)
        {
            assert(!_writeBuffer.empty());
            _D_Buffer = std::move(_writeBuffer.back());
            _writeBuffer.pop_back();
            _D_Buffer->reset();
        }
        _file.flush();
    }
    _file.flush();
}